%{
#include <stdio.h>
#include <string.h>
#include "tree.h"

extern char *yytext;
extern BODY *thebody;

extern int yylex();

extern int parseError;

void yyerror() {
   printf("syntax error before %s\n",yytext); 
}
%}

%union {
	int intconst;
	char *stringconst;
	struct FUNCTION *function;
	struct HEAD *head;
	struct TAIL *tail;
	struct TYPE *type;
	struct PAR_DECL_LIST *par_decl_list;
	struct VAR_DECL_LIST *var_decl_list;
	struct VAR_TYPE *var_type;
	struct BODY *body;
	struct DECL_LIST *decl_list;
	struct DECLARATION *declaration;
	struct STATEMENT_LIST *statement_list;
	struct STATEMENT *statement;
	struct OPT_LENGTH *opt_length;
	struct OPT_ELSE *opt_else;
	struct VARIABLE *variable;
	struct EXPRESSION *expression;
	struct TERM *term;
	struct ACT_LIST *act_list;
	struct EXP_LIST *exp_list;
}

%token <intconst> tNUM
%token <stringconst> tID
%token tFUNC tEND tINT tBOOL tARRAY tRECORD tTYPE tVAR tRETURN 
%token tWRITE tALLOCATE tIF tTHEN tWHILE tDO tOFLENGTH tELSE tTRUE 
%token tFALSE tNULL tEQ tNEQ tGEQ tLEQ tAND tOR

%type <function> function
%type <head> head
%type <tail> tail
%type <type> type
%type <par_decl_list> par_decl_list
%type <var_decl_list> var_decl_list
%type <var_type> var_type
%type <body> program body
%type <decl_list> decl_list
%type <declaration> declaration
%type <statement_list> statement_list
%type <statement> statement
%type <opt_length> opt_length
%type <opt_else> opt_else
%type <variable> variable
%type <expression> expression
%type <term> term
%type <act_list> act_list
%type <exp_list> exp_list

%start program
    
%left tOR
%left tAND
%left tEQ tNEQ
%left tLEQ tGEQ '<' '>'
%left '+' '-' 
%left '*' '/'
%right '!'
%nonassoc tNOELSE
%nonassoc tELSE
%%
program: body						{thebody = $1;};

function: head body tail{
						if(strcmp($1->id, $3->id)!=0){
							fprintf(stderr,"Error:%d: function '%s' doesn't share the same id with end\n", $1->lineno,$1->id);
							fprintf(stderr,"\tNote:%d: end of '%s' was here with id '%s'\n", $3->lineno,$1->id,$3->id);
							parseError++;
						}
						$$ = makeFUNC($1,$2,$3);
						};

head: tFUNC tID '(' par_decl_list ')' ':' type 	{$$ = makeHEAD($2,$4,$7);};

body: decl_list statement_list   				{$$ = makeBODY($1,$2);};

tail: tEND tID									{$$ = makeTAIL($2);};

type: tID										{$$ = makeTYPE_id($1);}
	| tINT										{$$ = makeTYPE_intconst();}
	| tBOOL										{$$ = makeTYPE_bool();}
	| tARRAY type								{$$ = makeTYPE_array($2);}
	| tRECORD '{' var_decl_list'}'				{$$ = makeTYPE_record($3);};
	
par_decl_list:  var_decl_list					{$$ = makePAR_DECL_LIST($1);}
			|									{$$ = NULL;};

var_decl_list : var_decl_list	',' var_type	{$$ = makeVAR_DECL_LIST($1,$3);}
		 | var_type								{$$ = makeVAR_DECL_LIST_BASE($1);};

var_type : tID ':' type							{$$ = makeVAR_TYPE($1,$3);};

decl_list: decl_list declaration				{$$ = makeDECL_LIST($1,$2);}
		 | 										{$$= NULL;};
		 
declaration: tTYPE tID '=' type ';'				{$$ = makeDECL_id($2,$4);}
		   | function							{$$ = makeDECL_func($1);}
		   | tVAR var_decl_list ';'				{$$ = makeDECL_varlist($2);};
		   
statement_list: statement						{$$ = makeSTAT_LIST_BASE($1);}
		 	  | statement_list statement 		{$$ = makeSTAT_LIST($1,$2);};
		 
statement: tRETURN expression ';'				{$$ = makeSTAT_RETURN($2);}
	| tWRITE expression ';'						{$$ = makeSTAT_WRITE($2);}
	| tALLOCATE variable opt_length ';'			{$$ = makeSTAT_ALLOCATE($2,$3);}
	| variable '=' expression ';'				{$$ = makeSTAT_VAR($1,$3);}
	| tIF expression tTHEN statement opt_else	{$$ = makeSTAT_IF($2,$4,$5);}
	| tWHILE expression tDO statement			{$$ = makeSTAT_WHILE($2,$4);}
	| '{' statement_list '}'					{$$ = makeSTAT_STAT_LIST($2);};
	
opt_length: tOFLENGTH expression				{$$ = makeLENGTH($2);}
	      |										{$$ = NULL;};
	  
opt_else: tELSE statement						{$$ = makeELSE($2);}
   		|	%prec tNOELSE						{$$ = NULL;};

variable: tID									{$$ = makeVAR_id($1);}
	    | variable '[' expression ']'			{$$ = makeVAR_exp($1,$3);}
     	| variable '.' tID						{$$ = makeVAR_dot($1,$3);};
	
expression:	expression tEQ expression 			{$$ = makeEXP_eq($1,$3);}
	|	expression tNEQ expression 				{$$ = makeEXP_neq($1,$3);}
	|	expression tGEQ expression 				{$$ = makeEXP_geq($1,$3);}
	|	expression tLEQ expression 				{$$ = makeEXP_leq($1,$3);}
	|	expression tAND expression 				{$$ = makeEXP_and($1,$3);}
	|	expression tOR expression 				{$$ = makeEXP_or($1,$3);}
	|	expression '+' expression  				{$$ = makeEXP_plus($1,$3);}	
	|	expression '-' expression 				{$$ = makeEXP_minus($1,$3);}
	|	expression '<' expression 				{$$ = makeEXP_lessthan($1,$3);}
	|	expression '>' expression 				{$$ = makeEXP_greaterthan($1,$3);}
	|	expression '*' expression 				{$$ = makeEXP_times($1,$3);}
	|	expression '/' expression 				{$$ = makeEXP_div($1,$3);}
	|	term									{$$ = makeEXP_term($1);};
	
term: variable									{$$ = makeTERM_var($1);}
	| tID '(' act_list ')'						{$$ = makeTERM_id($1,$3);}
	| '(' expression ')'						{$$ = makeTERM_exp($2);}
	| '!' term									{$$ = makeTERM_not($2);}
	| '|' expression '|'						{$$ = makeTERM_act_exp($2);}
	| tNUM										{$$ = makeTERM_num($1);}
	| tTRUE										{$$ = makeTERM_true();}
	| tFALSE									{$$ = makeTERM_false();}
	| tNULL										{$$ = makeTERM_null();};
	
act_list: exp_list								{$$ = makeACT_LIST($1);}
		|										{$$ = NULL;};
		
exp_list: expression							{$$ = makeEXP_LIST_BASE($1);}
		| exp_list ',' expression				{$$ = makeEXP_LIST($1,$3);};

%%